/**Results.java
 * 
 * Description:
 * Bean class for search information and search results.
 * 
 * Anping Zhou <anping.zhou@gmail.com>
 * Created: Jul 10, 2007
 */
package jp.ac.utokyo.pinyomi;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.PageContext;

/**
 * @author anping
 * 
 */
public class Results {
	
	// //////////////////////////////////////
	// Private fields
	// //////////////////////////////////////
	private double time = 0;	// search time cost
	private String query = "";	// search query
	private String method = "";	// what method will be used to search the dict
	private ArrayList<ResultRecord> resultSet = new ArrayList<ResultRecord>();	// result set
    private String action = "";	// Search or Clear. This field decides what to do next.
    private String dicttype = ""; // Dictionary type: jp or ch
    private String indexDir = "";
    private int len = 0;
    private boolean exactMatch = false;
    
    /**
     * Perform an action depends on the action fields.
     * @throws Exception 
     *
     */
    public void processRequest(HttpServletRequest request, PageContext pageContext) throws Exception {
    	// Set root path, related to which to locate data files.
    	Settings.Sets(pageContext.getServletContext().getRealPath("/") + "../../");
    	
    	getParameters(request);
        System.out.println("utf8 query:" + query);

        if ("Search".equals(action)){
        	performSearch();
        }
        else {
        	reset();
        }
    }
    
    
    /**
     * Perform an search action.
     * @throws Exception 
     *
     */
    private void performSearch() throws Exception{
    	if (query == null || query.equals("")) {
    		reset();
    		return;
    	}
    	
        Date start = new Date();
        
        query = query.trim();
        
        String formedQuery = formAQuery(method, query);
        System.err.println("formed query:" + formedQuery);

        if (getLen()==0){
        	// Designated the words length.(This is nothing with the exact mode or not)
        	resultSet = Searcher.search(method, formedQuery, this.indexDir);
        }
        
        // Set pinyomi score to resultset
        setPinyomiScore(query,resultSet);
        	
        // Resort by PinyomiScore
        Collections.sort(resultSet);
        
        
        Date end = new Date();
        setTime(end.getTime()-start.getTime());	// ms

    }

    /**
     * Set pinyomiScore to resultSet
     * @param queries Array of query.
     * @param resultSet
     * @throws IOException 
     */
    private void setPinyomiScore(String queries, ArrayList<ResultRecord> resultSet) throws IOException{
    	String[] sa = queries.split("[ |　]+");
    	
    	if("ch".equals(getDicttype())){
        	for(ResultRecord rr: resultSet){
        		rr.setPinyomiScore(ChPinyomiScorer.caclLogProb(sa, rr.getTranslation()));
        	}
    	} else if ("jp".equals(getDicttype())){
    		// TODO: Next Version - JpPinyomiScorer
        	for(ResultRecord rr: resultSet){
        		rr.setPinyomiScore(rr.getLuceneScore());
        	}
    	}
    }
    
    /**
     * form the query to a "0_boo 1_bar 2_foo" format
     * @param method String
     * @param query	String. raw query string.
     * @return
     */
    private String formAQuery(String method, String query){
    	String retv = "";
		query = query.replace('?', '#');

    	if ("char".equals(method) || "radical".equals(method) || "mappedHanzi".equals(method) || "mappedKanji".equals(method)){
    		// remove all spaces and Full spaces in the string
    		query = query.replaceAll("[ |　]*", "");
    		retv = chars2StrWithAnd(query.toCharArray());
    		
    	} else if ("onReading".equals(method)
    			|| "pinyinReading".equals(method)
    			|| "kunReading".equals(method)
    			|| "kanaReading".equals(method)
    			|| "stroke".equals(method)
    			|| "index".equals(method)){
    		retv = formAQuery(query);
    	}
    	return retv;
    }
    
    /**
     * Transform a sting into a string connected with "AND"
     * and having a sequence number for each eliment.<BR>
     * For example " boo bar foo " will be transformed into "0_boo AND 1_bar AND 2_foo"<BR>
     * The original string can be a string withou trimed.
     * @param query String	ex. " boo bar foo"
     * @return String "0_boo AND 1_bar AND 2_foo"
     */
    private String formAQuery(String query){
    	String retv = "";
    	
    	query = trim(query);
    	String[] sa = query.split("[ |　]+");
    	retv = array2StrWithAnd(sa);
    	
    	if (isExactMatch()){
    		retv = retv + " AND len="+sa.length;
    	}
    	return retv;
    }

    /**Trim all spaces and Full spaces
     * @param s	String.
     * @return String
     */
    public String trim(String s){
    	s = s.replaceAll("^[ |　]*", "");	
    	s = s.replaceAll("[ |　]*$", "");
    	return s;
    }
    
    /**
     * Transform an array of string into a string connected with "AND"
     * and having a sequence number for each eliment.<BR>
     * For example {"boo", "bar", "foo"} will be transformed into "0_boo AND 1_bar AND 2_foo"
     * @param sa String[]. ex. {"boo", "bar", "foo"}
     * @return String. ex. "0_boo AND 1_bar AND 2_foo"
     */
    private String array2StrWithAnd(String[] sa){
    	String retv = "";
    	
    	int i = 0;
    	for(String s: sa){
    		if(s.startsWith("len=")){
    			retv = retv + s + " AND ";	// len=3 AND ...
    		} else {
    			retv = retv + (i++) + "_" + s + " AND ";	// 0_BOO AND 1_BAR AND ... AND 
    		}
    	}
    	retv = retv.substring(0, retv.length()-5); // remove the last " AND "
    	return retv;
    }
    
    private String chars2StrWithAnd(char[] ca){
    	String retv = "";
    	
    	int i = 0;
    	for(char c: ca){
    		retv = retv + (i++) + "_" + c + " AND ";	// 0_日 AND 1_本 AND ... AND 
    	}
    	retv = retv.substring(0, retv.length()-5); // remove the last " AND "
    	return retv;
    	
    }
    
    /**
     * reset all private fields.
     *
     */
    private void reset(){
    	System.out.println("reset()");
    	setTime(0);
    	setQuery("");
    	setMethod("");
    	setLen(0);
    	resultSet.clear();
    }
    
    /**
     * Get paremeters from request.
     * @param request
     * @throws UnsupportedEncodingException 
     * @throws Exception 
     */
    private void getParameters(HttpServletRequest request) throws UnsupportedEncodingException {
    	setAction(request.getParameter("action"));
    	setDicttype(request.getParameter("dicttype"));
    	System.out.print(this.indexDir);
    	
    	setExactMatch(request.getParameter("exact")==null ? false : true);
    	setLen(getNumber(request.getParameter("len")));
    	
    	String s = request.getParameter("query");
    	setQuery((s==null) ? "" : new String(s.getBytes("ISO-8859-1"), "UTF-8").trim());
    	
    	setMethod(request.getParameter("method"));
    }
    
    private int getNumber(String s){
    	try{
    		return Integer.valueOf(s);
    	} catch (NumberFormatException e){
    		return 0;
    	}
    }

	/**
	 * Get the number of the results.(record number)
	 * @return record number
	 */
	public int getResultSetSize(){
		return this.resultSet.size();
	}
	
	public void setDicttype(String dicttype){
		this.dicttype = dicttype;
		if ("jp".equals(this.dicttype)) this.indexDir = Settings.JpLuceneIndex;
		else if("ch".equals(this.dicttype)) this.indexDir = Settings.ChLuceneIndex;
	}


	// //////////////////////////////////////
	// Normal GETTERs & SETTERs
	// //////////////////////////////////////
	public ArrayList<ResultRecord> getResultSet() {
		return resultSet;
	}
	public double getTime() {
		return time;
	}
	public void setTime(double time) {
		this.time = time;
	}
	public String getQuery() {
		return query;
	}
	public void setQuery(String query) {
		this.query = query;
	}
	public String getAction() {
		return action;
	}
	public void setAction(String submit) {
		this.action = submit;
	}

	public String getMethod() {
		return method;
	}

	public void setMethod(String method) {
		this.method = method;
	}


	public String getDicttype() {
		return dicttype;
	}


	public int getLen() {
		return len;
	}


	public void setLen(int len) {
		this.len = len;
	}


	public boolean isExactMatch() {
		return exactMatch;
	}


	public void setExactMatch(boolean exactMatch) {
		this.exactMatch = exactMatch;
	}


}
